<?php
/** Rho User Authorisation module.
* If you require user authorisation, require_once this file.
*
* @internal If a user is logged in, the conf setting 'auth.user' will be set to the user's id, otherwise it will hold FALSE.
*
* @package Rho
* @author Pete
* @version 0.1
* @license http://www.opensource.org/licenses/apache2.0.php Apache License, Version 2.0.*/

/** Load the auth module configuration file.*/
include_once Rho::$APP_PATH.'conf/auth.php';

/** Provides static methods to assist with authorisation.
* This class is never instantated.
*
* @package Rho */
final class Rho_Auth{


  private static $_driver;
  
  private static $_user;
  
  private function __construct(){}

  /** Attempts to log a user in.
  * As the method involves sending a cookie on success,
  * it MUST be called before any views have been loaded.
  *
  * @param string $username
  * @param string $password
  * @return bool TRUE on correct username and password.*/
  public static function logIn(& $controller, $username, $password, $remember=FALSE){
    $this->_loadDriver();
    $user=$this->_driver->logIn($username, $password);
    if(! $user) return FALSE;
    return TRUE;
  }
  
  public static function getUser(){
    return $this->_user;
  }
  
  public static function changePassword($new_password){
    $this->_loadDriver();
  }
  
  /** Logs the current user out.
  * As the method involves deleting a cookie,
  * it MUST be called before any views have been loaded.
  * @return bool TRUE on success.*/
  public static function logOut(){
    if(! Rho::conf('auth.user') ){
      // no-one logged in
      return FALSE;
    }
    $name=Rho::conf('auth.cookie_name', 'site_user_idi');
    return $this->_deleteCookie($name);
  }
  
  /** @return Rho_Auth_Driver The driver instance.*/
  public static function getDriverInstance(){
    $this->_loadDriver();
    return $this->_driver;
  }  
  
  private static function _loadDriver(){
    if(isset($this->_driver) ) return;
    $name='Auth_'.Rho::conf('auth.driver', 'DB').'_Driver';
    require_once Rho::$PATH.'rho/auth/'.$name.'.class.php';
    $this->_driver=new $name();
  }
  
  
  /** @ignore
  * This method is called once automatically when auth.inc.php is loaded
  * and should NOT be called.*/
  public static function _is_logged_in_via_cookie(){
    global $_COOKIE;
    $name=Rho::conf('auth.cookie_name', 'site_user_idi');
    $token=Rho::ifset($_COOKIE, $name, FALSE);
    if(! $token) return;
    /* ok token will have 4 parts, all of which are integers, seperated by colons.
    * a nonce, a timestamp, a user id, and hash */
    $parts=explode(':', $token, 4);
    if(count($parts)!=4){
      Rho::logAlert('Badly formed auth cookie value received', 2001);
      self::_deleteCookie($name);
      return;
    }
    $timestamp=round($parts[1]);
    if($timestamp < (time() - Rho::conf('auth.max_sess_time', 32400)) ){
      // very old cookie, disegard (though not worth logging) 
      self::_deleteCookie($name);
      return;
    }
    $nonce=round($parts[0]); #1st part
    $userid=$parts[2]; # 3rd part
    // check the hash matches
    if($parts[3] != self::_calcHash($nonce, $timestamp, $userid) ){
      Rho::logAlert('Invalid auth cookie value received', 2002);
      self::_deleteCookie($name);
      return;
    }
    Rho::setConf('auth.user' ,$userid);
  }
  
  private static function _calcHash(& $nonce, & $timestamp, & $userid){
    global $_SERVER;    
    $agent=Rho::ifset($_SERVER, 'HTTP_USER_AGENT', 'agent');
    // this is not a  RFC 2104 HMAC for speed reasons
    $key=str_pad(Rho::conf('secret'), 32, char(0) );
    return md5($key.md5($key.$nonce.$timestamp.$userid.$agent ) );
  }
  
  /** Deletes the auth cookie.
  *  Checks headers_sent() before trying to delete the cookie.
  * @param string $name the content of conf 'auth.cookie_name' */
  private static function _deleteCookie(& $name){
    if(headers_sent() ) return FALSE; # can't send cookie
    setcookie($name, '', (time() - 86400), Rho::conf('auth.cookie_path', '/') );
    return TRUE;
  }
  
}

/* See if there's a user logged in or not. */
Rho_Auth::_is_logged_in_via_cookie();


/** Interface for authorisation drivers.
* @see Rho_Auth
* @package Rho
* @subpackage Rho-auth */
interface Rho_Auth_Driver{

  /** Attempt to authorise a user.
  * @param string &$username
  * @param string &$password
  * @return bool|object The user object or FALSE on failure.*/
  public function logIn(& $username, & $password);
  
  /** If the driver supports the changing of passwords.
  * Normally TRUE.
  * @return bool */
  public function supportsChangePassword();

}